راهنمای جامع برای درک و پیادهسازی میانافزار TypeScript در برنامههای Express.js. الگوهای نوعی پیشرفته را برای کد قوی و قابل نگهداری بررسی کنید.
میانافزار TypeScript: تسلط بر الگوهای نوعی میانافزار Express
Express.js، یک چارچوب برنامه وب Node.js حداقلی و انعطافپذیر، به توسعهدهندگان اجازه میدهد تا APIها و برنامههای وب قوی و مقیاسپذیر بسازند. TypeScript با افزودن تایپ استاتیک، قابلیت نگهداری کد را بهبود میبخشد و خطاها را زودتر شناسایی میکند. توابع میانافزار یک رکن اساسی Express هستند و به شما امکان میدهند قبل از اینکه درخواستها به کنترلکنندههای مسیر شما برسند، آنها را رهگیری و پردازش کنید. این مقاله الگوهای نوعی پیشرفته TypeScript را برای تعریف و استفاده از میانافزار Express، بهبود ایمنی نوع و وضوح کد بررسی میکند.
درک میانافزار Express
توابع میانافزار توابعی هستند که به شی درخواست (req)، شی پاسخ (res) و تابع میانافزار بعدی در چرخه درخواست-پاسخ برنامه دسترسی دارند. توابع میانافزار میتوانند وظایف زیر را انجام دهند:
- اجرای هر کدی.
- ایجاد تغییرات در اشیاء درخواست و پاسخ.
- پایان دادن به چرخه درخواست-پاسخ.
- فراخوانی تابع میانافزار بعدی در پشته.
توابع میانافزار به ترتیب اجرا میشوند زیرا به برنامه Express اضافه میشوند. موارد استفاده رایج برای میانافزار عبارتند از:
- ثبت درخواستها.
- احراز هویت کاربران.
- مجوز دسترسی به منابع.
- اعتبارسنجی دادههای درخواست.
- رسیدگی به خطاها.
میانافزار اساسی TypeScript
در یک برنامه پایهای TypeScript Express، یک تابع میانافزار ممکن است به این صورت باشد:
import { Request, Response, NextFunction } from 'express';
function loggerMiddleware(req: Request, res: Response, next: NextFunction) {
console.log(`Request: ${req.method} ${req.url}`);
next();
}
export default loggerMiddleware;
این میانافزار ساده، متد درخواست و URL را در کنسول ثبت میکند. بیایید حاشیهنویسیهای نوع را تجزیه کنیم:
Request: نشاندهنده شی درخواست Express است.Response: نشاندهنده شی پاسخ Express است.NextFunction: تابعی که هنگام فراخوانی، میانافزار بعدی را در پشته اجرا میکند.
میتوانید از این میانافزار در برنامه Express خود به این صورت استفاده کنید:
import express from 'express';
import loggerMiddleware from './middleware/loggerMiddleware';
const app = express();
const port = 3000;
app.use(loggerMiddleware);
app.get('/', (req, res) => {
res.send('Hello, world!');
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
الگوهای نوعی پیشرفته برای میانافزار
در حالی که مثال میانافزار اساسی کاربردی است، اما فاقد انعطافپذیری و ایمنی نوع برای سناریوهای پیچیدهتر است. بیایید الگوهای نوعی پیشرفتهای را بررسی کنیم که توسعه میانافزار را با TypeScript بهبود میبخشد.
1. انواع درخواست/پاسخ سفارشی
اغلب، شما باید اشیاء Request یا Response را با ویژگیهای سفارشی گسترش دهید. به عنوان مثال، پس از احراز هویت، ممکن است بخواهید یک ویژگی user به شی Request اضافه کنید. TypeScript به شما امکان میدهد انواع موجود را با استفاده از ادغام اعلانی افزایش دهید.
// src/types/express/index.d.ts
import { Request as ExpressRequest } from 'express';
declare global {
namespace Express {
interface Request {
user?: {
id: string;
email: string;
// ... other user properties
};
}
}
}
export {}; // This is needed to make the file a module
در این مثال، ما رابط Express.Request را برای گنجاندن یک ویژگی اختیاری user افزایش میدهیم. اکنون، در میانافزار احراز هویت خود، میتوانید این ویژگی را پر کنید:
import { Request, Response, NextFunction } from 'express';
function authenticationMiddleware(req: Request, res: Response, next: NextFunction) {
// Simulate authentication logic
const userId = req.headers['x-user-id'] as string; // Or fetch from a token, etc.
if (userId) {
// In a real application, you would fetch the user from a database
req.user = {
id: userId,
email: `user${userId}@example.com`
};
next();
} else {
res.status(401).send('Unauthorized');
}
}
export default authenticationMiddleware;
و در کنترلکنندههای مسیر خود، میتوانید با خیال راحت به ویژگی req.user دسترسی پیدا کنید:
import express from 'express';
import authenticationMiddleware from './middleware/authenticationMiddleware';
const app = express();
const port = 3000;
app.use(authenticationMiddleware);
app.get('/profile', (req: Request, res: Response) => {
if (req.user) {
res.send(`Hello, ${req.user.email}! Your user ID is ${req.user.id}`);
} else {
// This should never happen if the middleware is working correctly
res.status(500).send('Internal Server Error');
}
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
2. کارخانههای میانافزار
کارخانههای میانافزار توابعی هستند که توابع میانافزار را برمیگردانند. این الگو زمانی مفید است که نیاز به پیکربندی میانافزار با گزینهها یا وابستگیهای خاص دارید. به عنوان مثال، یک میانافزار ورود به سیستم را در نظر بگیرید که پیامها را در یک فایل خاص ثبت میکند:
import { Request, Response, NextFunction } from 'express';
import fs from 'fs';
import path from 'path';
function createLoggingMiddleware(logFilePath: string) {
return (req: Request, res: Response, next: NextFunction) => {
const logMessage = `[${new Date().toISOString()}] Request: ${req.method} ${req.url}\n`;
fs.appendFile(logFilePath, logMessage, (err) => {
if (err) {
console.error('Error writing to log file:', err);
}
next();
});
};
}
export default createLoggingMiddleware;
میتوانید از این کارخانه میانافزار به این صورت استفاده کنید:
import express from 'express';
import createLoggingMiddleware from './middleware/loggingMiddleware';
const app = express();
const port = 3000;
const logFilePath = path.join(__dirname, 'logs', 'requests.log');
app.use(createLoggingMiddleware(logFilePath));
app.get('/', (req, res) => {
res.send('Hello, world!');
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
3. میانافزار ناهمزمان
توابع میانافزار اغلب نیاز به انجام عملیات ناهمزمان دارند، مانند پرس و جوهای پایگاه داده یا فراخوانی API. برای رسیدگی صحیح به عملیات ناهمزمان، باید اطمینان حاصل کنید که تابع next پس از اتمام عملیات ناهمزمان فراخوانی میشود. میتوانید با استفاده از async/await یا Promises به این هدف برسید.
import { Request, Response, NextFunction } from 'express';
async function asyncMiddleware(req: Request, res: Response, next: NextFunction) {
try {
// Simulate an asynchronous operation
await new Promise(resolve => setTimeout(resolve, 100));
console.log('Asynchronous operation completed');
next();
} catch (error) {
next(error); // Pass the error to the error handling middleware
}
}
export default asyncMiddleware;
مهم: به یاد داشته باشید که خطاها را در میانافزار ناهمزمان خود مدیریت کنید و آنها را با استفاده از next(error) به میانافزار رسیدگی به خطا ارسال کنید. این اطمینان میدهد که خطاها به درستی مدیریت و ثبت میشوند.
4. میانافزار رسیدگی به خطا
میانافزار رسیدگی به خطا نوع خاصی از میانافزار است که خطاهایی را که در طول چرخه درخواست-پاسخ رخ میدهند، مدیریت میکند. توابع میانافزار رسیدگی به خطا دارای چهار آرگومان هستند: err، req، res و next.
import { Request, Response, NextFunction } from 'express';
function errorHandler(err: any, req: Request, res: Response, next: NextFunction) {
console.error(err.stack);
res.status(500).send('Something went wrong!');
}
export default errorHandler;
شما باید میانافزار رسیدگی به خطا را بعد از تمام میانافزارهای دیگر و کنترلکنندههای مسیر ثبت کنید. Express میانافزار رسیدگی به خطا را با وجود چهار آرگومان شناسایی میکند.
import express from 'express';
import asyncMiddleware from './middleware/asyncMiddleware';
import errorHandler from './middleware/errorHandler';
const app = express();
const port = 3000;
app.use(asyncMiddleware);
app.get('/', (req, res) => {
throw new Error('Simulated error!'); // Simulate an error
});
app.use(errorHandler); // Error handling middleware MUST be registered last
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
5. میانافزار اعتبارسنجی درخواست
اعتبارسنجی درخواست یک جنبه حیاتی در ساخت APIهای امن و قابل اعتماد است. از میانافزار میتوان برای اعتبارسنجی دادههای درخواست ورودی استفاده کرد و اطمینان حاصل کرد که قبل از رسیدن به کنترلکنندههای مسیر شما، معیارهای خاصی را برآورده میکند. کتابخانههایی مانند joi یا express-validator را میتوان برای اعتبارسنجی درخواست استفاده کرد.
در اینجا یک مثال با استفاده از express-validator آورده شده است:
import { Request, Response, NextFunction } from 'express';
import { body, validationResult } from 'express-validator';
const validateCreateUserRequest = [
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
}
];
export default validateCreateUserRequest;
این میانافزار فیلدهای email و password را در بدنه درخواست اعتبارسنجی میکند. اگر اعتبارسنجی ناموفق باشد، یک پاسخ 400 Bad Request با آرایهای از پیامهای خطا برمیگرداند. میتوانید از این میانافزار در کنترلکنندههای مسیر خود به این صورت استفاده کنید:
import express from 'express';
import validateCreateUserRequest from './middleware/validateCreateUserRequest';
const app = express();
const port = 3000;
app.post('/users', validateCreateUserRequest, (req, res) => {
// If validation passes, create the user
res.send('User created successfully!');
});
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
6. تزریق وابستگی برای میانافزار
هنگامی که توابع میانافزار شما به سرویسها یا پیکربندیهای خارجی وابسته هستند، تزریق وابستگی میتواند به بهبود قابلیت آزمایش و نگهداری کمک کند. میتوانید از یک ظرف تزریق وابستگی مانند tsyringe استفاده کنید یا به سادگی وابستگیها را به عنوان آرگومان به کارخانههای میانافزار خود ارسال کنید.
در اینجا یک مثال با استفاده از یک کارخانه میانافزار با تزریق وابستگی آورده شده است:
// src/services/UserService.ts
export class UserService {
async createUser(email: string, password: string): Promise {
// In a real application, you would save the user to a database
console.log(`Creating user with email: ${email} and password: ${password}`);
await new Promise(resolve => setTimeout(resolve, 500)); // Simulate a database operation
}
}
// src/middleware/createUserMiddleware.ts
import { Request, Response, NextFunction } from 'express';
import { UserService } from '../services/UserService';
function createCreateUserMiddleware(userService: UserService) {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const { email, password } = req.body;
await userService.createUser(email, password);
res.status(201).send('User created successfully!');
} catch (error) {
next(error);
}
};
}
export default createCreateUserMiddleware;
// src/app.ts
import express from 'express';
import createCreateUserMiddleware from './middleware/createUserMiddleware';
import { UserService } from './services/UserService';
import errorHandler from './middleware/errorHandler';
const app = express();
const port = 3000;
app.use(express.json()); // Parse JSON request bodies
const userService = new UserService();
const createUserMiddleware = createCreateUserMiddleware(userService);
app.post('/users', createUserMiddleware);
app.use(errorHandler);
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});
بهترین شیوهها برای میانافزار TypeScript
- توابع میانافزار را کوچک و متمرکز نگه دارید. هر تابع میانافزار باید یک مسئولیت واحد داشته باشد.
- از نامهای توصیفی برای توابع میانافزار خود استفاده کنید. نام باید به وضوح نشان دهد که میانافزار چه کاری انجام میدهد.
- خطاها را به درستی مدیریت کنید. همیشه خطاها را catch کنید و آنها را با استفاده از
next(error)به میانافزار رسیدگی به خطا ارسال کنید. - از انواع درخواست/پاسخ سفارشی برای افزایش ایمنی نوع استفاده کنید. رابطهای
RequestوResponseرا با ویژگیهای سفارشی در صورت نیاز افزایش دهید. - از کارخانههای میانافزار برای پیکربندی میانافزار با گزینههای خاص استفاده کنید.
- توابع میانافزار خود را مستند کنید. توضیح دهید که میانافزار چه کاری انجام میدهد و چگونه باید از آن استفاده کرد.
- توابع میانافزار خود را به طور کامل تست کنید. برای اطمینان از اینکه توابع میانافزار شما به درستی کار میکنند، تست واحد بنویسید.
نتیجهگیری
TypeScript با افزودن تایپ استاتیک، بهبود قابلیت نگهداری کد و تشخیص زودهنگام خطاها، توسعه میانافزار Express را به طور قابل توجهی افزایش میدهد. با تسلط بر الگوهای نوعی پیشرفته مانند انواع درخواست/پاسخ سفارشی، کارخانههای میانافزار، میانافزار ناهمزمان، میانافزار رسیدگی به خطا و میانافزار اعتبارسنجی درخواست، میتوانید برنامههای Express قوی، مقیاسپذیر و ایمن از نظر نوع بسازید. به یاد داشته باشید که از بهترین شیوهها پیروی کنید تا توابع میانافزار خود را کوچک، متمرکز و به خوبی مستند شده نگه دارید.